
import { App } from "../../../../src/App";
import Map from "../../../../src/core/Map";
import { BaseLogic, PokerData, PokerType, PokerWeight, SpecialValue, Union, WEIGHT } from "../../../src/BaseLogic";

class CGameLogic extends BaseLogic {
    /**
     * 组合牌型
     *
     * @param {(number[] | PokerData[])} list
     * @returns {Map<PokerType,Union[]>}
     * @memberof CGameLogic
     */
    public join(list:number[] | PokerData[]):Map<PokerType,Union[]>{
        let out:Map<PokerType,Union[]> = this.split(list,false);
        let singles:Union[] = out.find(PokerType.single) ? out.find(PokerType.single).slice(0):[];
        let pairs:Union[] = out.find(PokerType.pairs) ? out.find(PokerType.pairs).slice(0):[];
        let three:Union[] = out.find(PokerType.three) ? out.find(PokerType.three).slice(0):[];
        let four:Union[] =  out.find(PokerType.bomb) ? out.find(PokerType.bomb).slice(0):[];
        let joker:Union[] =  out.find(PokerType.joker);
        joker && out.inset(PokerType.joker,joker);

        const inset = (head:PokerData[],sub:PokerData[],type:PokerType,weight:number)=>{
            const item:Union = {
                cards: head.slice(0).concat(sub),
                type:type,
                weight:weight,
                min:head[0].val
            };
            const find = out.find(type);
            find ? find.push(item) : out.inset(type,[]).push(item);
        };

        if (three.length > 0){
            for (let i = 0; i < three.length-1; ++i){
                const threeCards = three[i].cards.slice(0);
                // 飞机
                if (Math.abs(threeCards[0].val - three[i+1].cards[0].val) === 1 && three[i+1].cards[0].val < SpecialValue.num_2){
                    inset(threeCards,three[i+1].cards,PokerType.plane,PokerWeight.plane);
                }

                // 三带对
                for(let it of pairs){
                    if (it.cards[0].val < SpecialValue.num_2)
                        inset(threeCards,it.cards,PokerType.three_pairs,PokerWeight.three_pairs);
                }

                // 三带一
                for (let it of singles){
                    if (it.cards[0].val < SpecialValue.num_2)
                        inset(threeCards,it.cards,PokerType.three_single,PokerWeight.three_single);
                }
            }
        }
        if (four.length > 0){
            for (let i = 0; i < four.length; ++i){
                const fourCards = four[i].cards.slice(0);
                // 四带二
                for (let i = 0; i < singles.length-2; i+=2){
                    if (singles[i+1].cards[0].val < SpecialValue.num_2)
                        inset(fourCards,[singles[0].cards[0],singles[i+1].cards[0]],PokerType.four_Single,PokerWeight.four_Single);
                }

                // 四带对
                for (let i = 0; i < pairs.length-2; i+=2){
                    if (pairs[i+1].cards[0].val < SpecialValue.num_2){
                        let cards = [pairs[0].cards[0],pairs[0].cards[1],pairs[i+1].cards[0],pairs[i+1].cards[1]];
                        inset(fourCards,cards,PokerType.four_pairs,PokerWeight.four_pairs);
                    }
                }
            }
        }

        let planes = out.find(PokerType.plane) ? out.find(PokerType.plane).slice(0):[];
        if (planes.length > 0){
            for (let i = 0; i < planes.length; ++i){
                const planeCards = planes[i].cards.slice(0);
    
                // 飞机带
                for (let i = 0; i < singles.length-2; i+=2){
                    if (singles[i+1].cards[0].val < SpecialValue.num_2)
                        inset(planeCards,[singles[0].cards[0],singles[i+1].cards[0]],PokerType.plane_single,PokerWeight.plane_single);
                }
                for (let i = 0; i < pairs.length-2; i+=2){
                    if (pairs[i].cards[0].val < SpecialValue.num_2){
                        inset(planeCards,pairs[i].cards,PokerType.plane_pair,PokerWeight.plane_pair);
                        if (pairs[i+1].cards[0].val < SpecialValue.num_2){
                            let cards = [pairs[0].cards[0],pairs[0].cards[1],pairs[i+1].cards[0],pairs[i+1].cards[1]];
                            inset(planeCards,cards,PokerType.plane_pairs,PokerWeight.plane_pairs);
                        }
                    }
                }
            }
        }

        // 单顺/双顺
        if (list.length < 5) return out;
        
        return out;
    }
    /**
     * 评估手牌价值,将其转化为抢地主的分数
     * @param list 
     * @returns 
     */
    public appraise(list:PokerData[]):number{
        let _score = 0;
        const splice = this.split(list).iterable;
        for (let it of splice){
            it.Key === PokerType.joker && (_score += 8); // 王炸
            it.Key === PokerType.bomb && (_score += (4*it.Val.length)); // 炸弹分
            if (it.Key === PokerType.three || it.Key === PokerType.pairs || it.Key === PokerType.single){
                it.Key === PokerType.three && (_score += 2*it.Val.length);
                for (let v of it.Val){
                    v.cards[0].val === SpecialValue.num_2 && (_score += it.Val.length);
                    v.cards[0].val === SpecialValue.imp && (_score += 1);
                    v.cards[0].val === SpecialValue.king && (_score += 2);
                }
            }
        }

        if (_score >= 10)_score = 3;
        else if (_score >= 5 && _score < 7) _score = 2;
        else if (_score >= 3 && _score < 5) _score = 1;
        else _score = 0;
        return _score;
    }

    public isValid(src:PokerData[],des:PokerData[]):boolean{
        const tmpSrc = this.getUnion(src);
        const tmpDes = this.getUnion(des);
        if (tmpDes.type === PokerType.joker) return false;
        if (tmpDes.type === PokerType.unknow) return tmpSrc.type !== PokerType.unknow;

        if (tmpSrc.type === PokerType.joker || (tmpSrc.type === PokerType.bomb && tmpDes.type !== PokerType.bomb)) return true;

        if (src.length  === des.length){
            //普通炸弹
            if (tmpSrc.type === PokerType.bomb){
                if (tmpDes.type === PokerType.bomb)
                    return tmpSrc.cards[0].val > tmpDes.cards[0].val;
                else return true;
            }

            //所选牌不符合规定
            if (tmpSrc.type !== tmpDes.type) return false;
            
            // 牌型相同，则比较牌值 333A < 4443
            return this.valCount(src)[0].val > this.valCount(des)[0].val;
        }

        return false;
    }

    /**
     * 查找所有可以出的牌型
     * 牌组优先级: 双顺 > 单顺 > 四带 > 三带 > 对子 > 单牌 > 炸弹 > 飞机
     * @param {PokerData[]} list
     * @param {PokerData[]} [follow]
     * @param {boolean} [isSplit] 是否拆牌
     * @returns {Union[]}
     * @memberof CGameLogic
     */
    public findCanDiscard(list:PokerData[],follow?:PokerData[],isSplit:boolean=false):Union[]{
        const tmpFollow:Union = this.getUnion(follow);
        if (tmpFollow.type === PokerType.joker){
            return null;
        }

        const allUnion:Map<PokerType,Union[]> = this.join(list);
        let low:Union[] = [];
        let hight:Union[] = [];
        let out:Union[] = [];
        if (!follow || follow.length < 1){
            out = allUnion.valueToArray();
        } else {
            let subs = allUnion.find(tmpFollow.type) || [];
            for (let it of subs){
                it.min > tmpFollow.min && out.push(it);
            }

            // 是否拆三张
            if (tmpFollow.type === PokerType.pairs || tmpFollow.type === PokerType.single){
                subs = allUnion.find(PokerType.three) || [];
                for (let it of subs){
                    if (it.min > tmpFollow.min){
                        if (isSplit){
                            const _type = tmpFollow.type === PokerType.pairs ? PokerType.pairs : PokerType.single;
                            const _weight = tmpFollow.type === PokerType.pairs ? PokerWeight.pairs : PokerWeight.single;
                            out.push({cards:[it.cards[0],it.cards[1]],type:_type,weight:_weight,min:it.cards[0].val});
                        } else out.push(it);
                    }
                }
            }

            // 炸弹不拆
            subs = allUnion.find(PokerType.bomb) || [];
            for (let it of subs){
                if (tmpFollow.type !== PokerType.bomb || (tmpFollow.type === PokerType.bomb && it.min > tmpFollow.min)){
                    out.push(it);
                }
            }

            // 压入王炸
            subs = allUnion.find(PokerType.joker);
            subs && out.push(subs[0]);

            if (out.length < 1){
                return null;
            }
        }

        for (let it of out){
            (it.type === PokerType.bomb || it.type === PokerType.joker || it.type >= PokerType.plane) ? hight.push(it) : low.push(it);
        }
        low.sort((a,b)=>{
            return a.type === b.type ? a.min-b.min : b.type-a.type;
        });
        hight.sort((a,b)=>{
            return a.type === b.type ? a.min-b.min : a.type-b.type;
        });

        out = App.Util.concatArray(low,hight);
        return out.length > 0 ? out : null;

    }
}
const GameLogic:CGameLogic = CGameLogic.getInstance<CGameLogic>(CGameLogic);
export default GameLogic;
